Skip to content

feat(sender): let a manually added Miracast receiver specify its port - #40

Merged
Developer1010x merged 2 commits into
masterfrom
feat/sender-manual-port
Aug 26, 2026
Merged

feat(sender): let a manually added Miracast receiver specify its port#40
Developer1010x merged 2 commits into
masterfrom
feat/sender-manual-port

Conversation

@snadahalli

Copy link
Copy Markdown
Owner

The gap

"+ AirPlay IP" has always had a Port field. "+ Miracast IP" did not — it hardcoded 7236:

mode: MiracastMode::Infrastructure { addr: ip, port: 7236 },

So a sink listening anywhere else could not be reached by hand at all. This adds the field, defaulted to 7236.

It is hidden when "Wi-Fi Direct" is ticked, because a P2P sink has no address — let alone a port — until the group forms. Asking for one there would be asking for a value that cannot exist yet.

A bug found while adding it

The AirPlay field was parsing its value like this:

let port: u16 = self.airplay_port.parse().unwrap_or(7000);

70000 overflows a u16 and abc is not a number, so both silently became 7000. The dialog reported success, and the cast then failed against a port the user never chose — with the failure surfacing far from the typo that caused it.

parse_port replaces it and additionally rejects 0, which parses fine as a u16 but asks the OS for an ephemeral port — meaningless in a destination address.

Dialogs no longer discard rejected input

Both dialogs used to close unconditionally on Add, so a mistyped IP dismissed the window and threw away everything typed, leaving Invalid IP address in the status bar with nothing left to correct.

That was already poor; it becomes untenable once a second field can fail validation, since an Invalid port message would appear with the dialog already gone. Both now stay open unless the entry was accepted.

Tests

5 new unit tests on parse_port, including one case per input the old unwrap_or swallowed (70000, abc, empty, whitespace-only, -1, 70.00), plus port 0 and whitespace tolerance.

fmt --check clean, clippy --all-targets --all-features -D warnings clean, cargo test --all 221 passed / 0 failed.

Left alone deliberately

The port: 7236 in the Wi-Fi Direct discovery path stays. That is a placeholder for a peer that has no addresses yet (addresses: vec![]), not a user-supplied value.

The "+ AirPlay IP" dialog has always had a Port field. "+ Miracast IP" did
not, and hardcoded 7236, so a sink listening anywhere else could not be
reached by hand at all. Add the field, defaulted to 7236 and hidden when
"Wi-Fi Direct" is ticked, since a P2P sink has no address — let alone a port
— until the group forms.

While adding it, fix what the AirPlay field was doing with its value:

    let port: u16 = self.airplay_port.parse().unwrap_or(7000);

`70000` overflows a `u16` and `abc` is not a number, so both silently became
7000. The dialog then reported success and the cast failed later against a
port the user never chose — the failure surfacing far from the typo that
caused it. Port 0 parses fine but asks the OS for an ephemeral port, which is
meaningless in a destination address, so it is rejected too. `parse_port`
now covers all of these and is unit tested, including one case per input the
old `unwrap_or` swallowed.

Both dialogs also stop dismissing themselves when the entry is rejected.
Previously a bad IP closed the dialog and discarded what had been typed,
leaving the complaint stranded in the status bar with nothing left to correct
— which becomes worse once a second field can fail validation.

The remaining `port: 7236` in the Wi-Fi Direct discovery path is left alone:
that is a placeholder for a peer with no addresses yet, not a user-supplied
value.
Adding the Port field was not enough: the value reached
`MiracastMode::Infrastructure` and `DiscoveredReceiver::addr()` correctly, and
was then thrown away one call later.

    // app.rs — holds the full SocketAddr, passes only the IP
    start_miracast_cast(addr.ip(), ...)

    // casting.rs — re-attaches the hardcoded default
    let sink_addr = SocketAddr::new(receiver_ip, MIRACAST_RTSP_PORT);  // 7236

So entering 7290 produced "Failed to connect to 192.168.0.105:7236", naming a
port the user never typed. That is the same defect class as the receiver's
"Listening on port N" — an interface reporting something that was not true.

Take a `SocketAddr` in `start_miracast_cast` rather than an `IpAddr`, and pass
`addr` unchanged. The lossy step is now impossible to reintroduce without a
type error, since there is no longer an `IpAddr` parameter to drop the port
into. `MIRACAST_RTSP_PORT` goes with it; the default for the dialog already
lives in `DEFAULT_MIRACAST_PORT`.

The AirPlay path was already correct — `start_airplay_cast` has always taken a
`SocketAddr`.
@Developer1010x
Developer1010x merged commit 8d489aa into master Aug 26, 2026
5 checks passed
Developer1010x pushed a commit that referenced this pull request Aug 26, 2026
…43)

#42 was merged twelve seconds after #41, into `fix/airplay-hkp-header` —
which #41 had just merged into master and left behind. The merge succeeded,
so nothing looked wrong, but the commits landed on a branch nothing points at
and master never received them.

Master therefore has the `X-Apple-HKP` header from #41 and none of what it was
a prerequisite for: the SRP proof still hashes g padded, transient pairing
still runs M5/M6 and gets the connection closed, and there is no encrypted
control channel. Pairing is broken on master in exactly the way #42 fixed.

This restores #42's own diff — the eight `openplay-airplay` files it actually
touched — on top of current master.

Deliberately *not* a merge of `fix/airplay-hkp-header`. That branch was cut
before #35, #36, #37, #39 and #40 merged, so a diff against it reads as
deleting `openplay-discovery/src/address.rs` and reverting 244 lines of
`openplay-sender/src/app.rs`. Merging it would silently undo five landed
fixes. Only the range between #42 and its own parent is safe to replay, and
that range touches nothing outside `openplay-airplay`.

Verified after the replay: 233 tests pass, clippy and fmt clean, and the work
from #35/#36/#37/#40 is still in the tree.

Co-authored-by: Sandeepa Nadahalli <1698507+snadahalli@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants